package com.hmall.search.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hmall.common.client.ItemClient;
import com.hmall.common.dto.Item;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.ISearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchService implements ISearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ItemClient itemClient;


    @Override
    public List<String> getSuggestion(String prefix) {
        try {
            //1.准备request
            SearchRequest request = new SearchRequest("item");
            //2.准备dsl
            request.source().suggest(new SuggestBuilder()
                    .addSuggestion("my_suggestion",SuggestBuilders
                            .completionSuggestion("suggestion")
                            .skipDuplicates(true)
                            .size(10)
                            .prefix(prefix))
            );
            //3.发送请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            //4.处理相应
            //4.1获取suggestion
            CompletionSuggestion suggestion = response.getSuggest().getSuggestion("my_suggestion");
            //4.2获取options
            List<CompletionSuggestion.Entry.Option> options = suggestion.getOptions();
            //4.3循环遍历,获取text
            ArrayList<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                list.add(option.getText().string());
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Map<String, List<String>> getFilters(RequestParams params) {
        try {
            //1.准备request
            SearchRequest request = new SearchRequest("item");
            //2.准备dsl
            //2.1不要文档数据
            request.source().size(0);
            //2.2query条件
            buildBasicQuery(request,params);
            //2.3聚合条件
            request.source().aggregation(
                    AggregationBuilders.terms("brandAgg").field("brand").size(20)
            );
            request.source().aggregation(
                    AggregationBuilders.terms("categoryAgg").field("category").size(20)
            );
            //3.发请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            //4.解析结果
            HashMap<String,List<String>> map = new HashMap<>(2);
            Aggregations aggregations = response.getAggregations();
            //4.1根据名称获取品牌聚合
            Terms brandAgg = aggregations.get("brandAgg");
            ArrayList<String> brandList = new ArrayList<>();
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                String key = bucket.getKeyAsString();
                brandList.add(key);
            }
            map.put("brand",brandList);
            //4.2根据名称获取分类聚合
            Terms categoryAgg = aggregations.get("categoryAgg");
            ArrayList<String> categoryList = new ArrayList<>();
            for (Terms.Bucket bucket : categoryAgg.getBuckets()) {
                String key = bucket.getKeyAsString();
                categoryList.add(key);
            }
            map.put("categoryAgg",categoryList);
            return map;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public PageDTO<ItemDoc> list(RequestParams params) {
        try {
            //1.准备request
            SearchRequest request = new SearchRequest("item");
            //2.准备sql
            //2.1query条件
            buildBasicQuery(request,params);
            //2.2分页
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page - 1) * size).size(size);
            //2.3排序
            String sortBy = params.getSortBy();
            //销量倒序排列
            if("sold".equals(sortBy)){
                request.source().sort(sortBy,SortOrder.DESC);
                //价格正序排列
            }else if("price".equals(sortBy)){
                request.source().sort(sortBy,SortOrder.ASC);
            }
            //2.4高亮
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
            //3.发请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            //4.解析结果
            SearchHits searchHits = response.getHits();
            //4.1设置total
            long total = searchHits.getTotalHits().value;
            PageDTO<ItemDoc> dto = new PageDTO<>();
            dto.setTotal(total);
            //4.2数据
            SearchHit[] hits = searchHits.getHits();
            //4.3遍历
            ArrayList<ItemDoc> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                //4.4获取source
                String json = hit.getSourceAsString();
                //4.5转java
                ItemDoc doc = objectMapper.readValue(json, ItemDoc.class);
                //4.6获取高亮
                Map<String, HighlightField> map = hit.getHighlightFields();
                if(map != null && !map.isEmpty()){
                    HighlightField name = map.get("name");
                    if(name !=null){
                        Text[] fragments = name.getFragments();
                        if(fragments != null && fragments.length >= 1){
                            Text fragment = fragments[0];
                            doc.setName(fragment.string());
                        }
                    }
                }
                list.add(doc);
            }
            dto.setList(list);
            return dto;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveItemById(Long id) {
        try {
            Item item = itemClient.queryItemById(id);
            if(item != null){
                restHighLevelClient.index(new IndexRequest("item")
                        .id(id.toString()).source(objectMapper.writer().writeValueAsString(item),XContentType.JSON)
                        ,RequestOptions.DEFAULT);

            }
        } catch (IOException e) {
            throw new RuntimeException("添加es数据失败",e);
        }
    }

    @Override
    public void deleteItemById(Long id) {
        try {
            restHighLevelClient.delete(new DeleteRequest("item",id.toString()),RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("删除数据失败" + id,e);
        }
    }

    private void buildBasicQuery(SearchRequest request, RequestParams params) {
        //1.创建布尔查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //1.1 key
        String key = params.getKey();
        if(StringUtils.isNotEmpty(key)){
            //非空
            boolQuery.must(QueryBuilders.matchQuery("all",key));
        }else {
            //空
            boolQuery.must(QueryBuilders.matchAllQuery());
        }

        //1.2 brand
        String brand = params.getBrand();
        if (StringUtils.isNotEmpty(brand)){
            boolQuery.filter(QueryBuilders.termQuery("brand",brand));
        }
        //1.3 category
        String category = params.getCategory();
        if (StringUtils.isNotEmpty(category)){
            boolQuery.filter(QueryBuilders.termQuery("category",category));
        }
        //1.4 price
        Long minPrice = params.getMinPrice();
        Long maxPrice = params.getMaxPrice();
        if(minPrice != null && maxPrice !=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice * 100).lte(maxPrice * 100));
        }

        //算分函数查询 广告位处理
        FunctionScoreQueryBuilder query = QueryBuilders.functionScoreQuery(boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(100)
                        )
                });


        //2. 放入request
        request.source().query(query);
    }
}
